package com.yox.rpc.transport.socket.util;

import com.yox.rpc.entity.RpcRequest;
import com.yox.rpc.entity.RpcResponse;
import com.yox.rpc.enumeration.PackageType;
import com.yox.rpc.enumeration.RpcError;
import com.yox.rpc.exception.RpcException;
import com.yox.rpc.serializer.CommonSerializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;

/**
 * 统一传输协议，使用CommonDecoder协议，并且使用序列化器进行序列化
 * Socket 方式从输入流中按照CommonDecoder协议读取字节，并使用序列化器serializer反序列化
 *
 * +---------------+---------------+-----------------+-------------+
 * |  Magic Number |  Package Type | Serializer Type | Data Length |
 * |    4 bytes    |    4 bytes    |     4 bytes     |   4 bytes   |
 * +---------------+---------------+-----------------+-------------+
 * |                          Data Bytes                           |
 * |                   Length: ${Data Length}                      |
 * +---------------------------------------------------------------+
 * 首先是 4 字节魔数，表识一个协议包。接着是 Package Type，标明这是一个调用请求还是调用响应，Serializer Type 标明了实际数据使用的序列化器，
 * 这个服务端和客户端应当使用统一标准；Data Length 就是实际数据的长度，设置这个字段主要防止粘包，
 * 最后就是经过序列化后的实际数据，可能是 RpcRequest 也可能是 RpcResponse 经过序列化后的字节，取决于 Package Type。
 */
public class ObjectReader {
    private static final Logger logger = LoggerFactory.getLogger(ObjectReader.class);
    private static final int MAGIC_NUMBER = 0xCAFEBABE;

    public static Object readObject(InputStream in) throws IOException {

        // 读取 4 bytes 的魔数
        byte[] numberBytes = new byte[4];
        in.read(numberBytes);
        int magic = bytesToInt(numberBytes);
        if (magic != MAGIC_NUMBER) {
            logger.error("不识别的协议包：{}", magic);
            throw new RpcException(RpcError.UNKNOWN_PROTOCOL);
        }

        // 读取 4 bytes 的包类型
        in.read(numberBytes);
        int packageCode = bytesToInt(numberBytes);
        Class<?> packageClass;
        if (packageCode == PackageType.REQUEST_PACK.getCode()) {
            packageClass = RpcRequest.class;
        } else if (packageCode == PackageType.RESPONSE_PACK.getCode()) {
            packageClass = RpcResponse.class;
        } else {
            logger.error("不识别的数据包：{}", packageCode);
            throw new RpcException(RpcError.UNKNOWN_PACKAGE_TYPE);
        }

        // 读取 4 bytes 的序列化器编号
        in.read(numberBytes);
        int serializerCode = bytesToInt(numberBytes);
        CommonSerializer serializer = CommonSerializer.getByCode(serializerCode);
        if (serializer == null) {
            logger.error("不识别的反序列化器：{}", serializerCode);
            throw new RpcException(RpcError.UNKNOWN_SERIALIZER);
        }

        // 读取 4 bytes 的数据包长度
        in.read(numberBytes);
        int length = bytesToInt(numberBytes);

        // 读取数据
        byte[] bytes = new byte[length];
        in.read(bytes);

        // 使用序列化器进行反序列化并返回
        return serializer.deserialize(bytes, packageClass);
    }

    /**
     * 将字节数组转换为 int 类型
     * @param src 字节数组
     * @return [int]
     */
    private static int bytesToInt(byte[] src) {
        int value;
        value = ((src[0] & 0xFF) << 24)
                | ((src[1] & 0xFF) << 16)
                | ((src[2] & 0xFF) << 8)
                | (src[3] & 0xFF);
        return value;
    }

}
